/*!
 * \file WtDataManager.cpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief 数据管理器实现文件
 * 
 * \details 本文件实现了WonderTrader数据服务系统的数据管理器类WtDataManager：
 *          - 实现IRdmDtReaderSink接口，提供数据读取回调功能
 *          - 管理历史数据和实时数据的访问和缓存
 *          - 支持K线数据、Tick数据、Level2数据的查询
 *          - 提供按时间范围和数量的数据切片功能
 *          - 实现数据缓存机制，提高查询性能
 *          - 集成基础数据管理器和热点管理器
 *          - 支持多种K线周期和数据格式
 */
#include "WtDataManager.h"
#include "WtDtRunner.h"
#include "WtHelper.h"

#include "../Includes/WTSDataDef.hpp"
#include "../Includes/WTSVariant.hpp"
#include "../Includes/WTSContractInfo.hpp"

#include "../Share/StrUtil.hpp"
#include "../Share/TimeUtils.hpp"
#include "../Share/CodeHelper.hpp"
#include "../Share/DLLHelper.hpp"

#include "../WTSTools/WTSLogger.h"
#include "../WTSTools/WTSDataFactory.h"


WTSDataFactory g_dataFact;

/**
 * @brief 构造函数
 * @details 初始化数据管理器，设置所有指针为NULL
 */
WtDataManager::WtDataManager()
	: _bd_mgr(NULL)
	, _hot_mgr(NULL)
	, _runner(NULL)
	, _reader(NULL)
{
}

/**
 * @brief 析构函数
 * @details 清理数据管理器资源，释放K线缓存中的数据
 */
WtDataManager::~WtDataManager()
{
	// 释放K线缓存中的所有数据
	for(auto& m : _bars_cache)
	{
		if (m.second._bars != NULL)
			m.second._bars->release();
	}
	_bars_cache.clear();
}

/**
 * @brief 初始化数据存储
 * @details 根据配置初始化数据存储相关组件，加载数据读取器模块
 * @param cfg 配置参数
 * @return bool 初始化是否成功
 */
bool WtDataManager::initStore(WTSVariant* cfg)
{
	if (cfg == NULL)
		return false;

	// 获取数据存储模块名称，默认为WtDataStorage
	std::string module = cfg->getCString("module");
	if (module.empty())
		module = "WtDataStorage";

	// 构建完整的模块路径
	module = WtHelper::get_module_dir() + DLLHelper::wrap_module(module.c_str());
	DllHandle libParser = DLLHelper::load_library(module.c_str());
	if (libParser)
	{
		// 获取创建数据读取器的函数指针
		FuncCreateRdmDtReader pFuncCreateReader = (FuncCreateRdmDtReader)DLLHelper::get_symbol(libParser, "createRdmDtReader");
		if (pFuncCreateReader == NULL)
		{
			WTSLogger::error("Initializing of random data reader failed: function createRdmDtReader not found...");
		}

		// 获取删除数据读取器的函数指针
		FuncDeleteRdmDtReader pFuncDeleteReader = (FuncDeleteRdmDtReader)DLLHelper::get_symbol(libParser, "deleteRdmDtReader");
		if (pFuncDeleteReader == NULL)
		{
			WTSLogger::error("Initializing of random data reader failed: function deleteRdmDtReader not found...");
		}

		// 创建数据读取器实例
		if (pFuncCreateReader && pFuncDeleteReader)
		{
			_reader = pFuncCreateReader();
			_remover = pFuncDeleteReader;
		}

	}
	else
	{
		WTSLogger::error_f("Initializing of random data reader failed: loading module {} failed...", module);

	}

	// 初始化数据读取器
	_reader->init(cfg, this);
	return true;
}

/**
 * @brief 初始化数据管理器
 * @details 根据配置参数初始化数据管理器及相关组件
 * @param cfg 配置参数
 * @param runner 数据运行器指针
 * @return bool 初始化是否成功
 */
bool WtDataManager::init(WTSVariant* cfg, WtDtRunner* runner)
{
	_runner = runner;
	if (_runner)
	{
		_bd_mgr = &_runner->getBaseDataMgr();
		_hot_mgr = &_runner->getHotMgr();
	}

	return initStore(cfg->get("store"));
}

/**
 * @brief 数据读取模块日志
 * @details 实现IRdmDtReaderSink接口，处理数据读取器的日志输出
 * @param ll 日志级别
 * @param message 日志消息
 */
void WtDataManager::reader_log(WTSLogLevel ll, const char* message)
{
	WTSLogger::log_raw(ll, message);
}

/**
 * @brief 按时间范围获取Tick数据切片
 * @details 根据指定的时间范围获取Tick数据切片，时间戳转换为微秒精度
 * @param stdCode 标准合约代码
 * @param stime 开始时间
 * @param etime 结束时间（0表示到最新）
 * @return WTSTickSlice* Tick数据切片
 */
WTSTickSlice* WtDataManager::get_tick_slices_by_range(const char* stdCode,uint64_t stime, uint64_t etime /* = 0 */)
{
	// 将时间戳转换为微秒精度
	stime = stime * 100000;
	etime = etime * 100000;
	return _reader->readTickSlicesByRange(stdCode, stime, etime);
}

/**
 * @brief 获取委托队列数据切片
 * @details 根据时间范围获取委托队列数据切片，时间戳转换为微秒精度
 * @param stdCode 标准合约代码
 * @param stime 开始时间
 * @param etime 结束时间（0表示到最新）
 * @return WTSOrdQueSlice* 委托队列数据切片
 */
WTSOrdQueSlice* WtDataManager::get_order_queue_slice(const char* stdCode,uint64_t stime, uint64_t etime /* = 0 */)
{
	// 将时间戳转换为微秒精度
	stime = stime * 100000;
	etime = etime * 100000;
	return _reader->readOrdQueSliceByRange(stdCode, stime, etime);
}

/**
 * @brief 获取委托明细数据切片
 * @details 根据时间范围获取委托明细数据切片，时间戳转换为微秒精度
 * @param stdCode 标准合约代码
 * @param stime 开始时间
 * @param etime 结束时间（0表示到最新）
 * @return WTSOrdDtlSlice* 委托明细数据切片
 */
WTSOrdDtlSlice* WtDataManager::get_order_detail_slice(const char* stdCode,uint64_t stime, uint64_t etime /* = 0 */)
{
	// 将时间戳转换为微秒精度
	stime = stime * 100000;
	etime = etime * 100000;
	return _reader->readOrdDtlSliceByRange(stdCode, stime, etime);
}

/**
 * @brief 获取逐笔成交数据切片
 * @details 根据时间范围获取逐笔成交数据切片，时间戳转换为微秒精度
 * @param stdCode 标准合约代码
 * @param stime 开始时间
 * @param etime 结束时间（0表示到最新）
 * @return WTSTransSlice* 逐笔成交数据切片
 */
WTSTransSlice* WtDataManager::get_transaction_slice(const char* stdCode,uint64_t stime, uint64_t etime /* = 0 */)
{
	// 将时间戳转换为微秒精度
	stime = stime * 100000;
	etime = etime * 100000;
	return _reader->readTransSliceByRange(stdCode, stime, etime);
}

/**
 * @brief 获取交易时段信息
 * @details 根据时段ID或合约代码获取交易时段信息
 * @param sid 时段ID或合约代码
 * @param isCode 是否为合约代码
 * @return WTSSessionInfo* 交易时段信息指针
 */
WTSSessionInfo* WtDataManager::get_session_info(const char* sid, bool isCode /* = false */)
{
	if (!isCode)
		return _bd_mgr->getSession(sid);

	// 根据合约代码获取品种信息，再获取交易时段
	WTSCommodityInfo* cInfo = _bd_mgr->getCommodity(CodeHelper::stdCodeToStdCommID(sid).c_str());
	if (cInfo == NULL)
		return NULL;

	return _bd_mgr->getSession(cInfo->getSession());
}

/**
 * @brief 按时间范围获取K线数据切片
 * @details 根据指定的时间范围获取K线数据切片，支持K线缓存机制
 * @param stdCode 标准合约代码
 * @param period K线周期
 * @param times 周期倍数
 * @param stime 开始时间
 * @param etime 结束时间（0表示到最新）
 * @return WTSKlineSlice* K线数据切片
 */
WTSKlineSlice* WtDataManager::get_kline_slice_by_range(const char* stdCode, WTSKlinePeriod period, uint32_t times,uint64_t stime, uint64_t etime /* = 0 */)
{
	std::string key = StrUtil::printf("%s-%u", stdCode, period);

	// 如果倍数为1，直接从数据读取器获取数据
	if (times == 1)
	{
		return _reader->readKlineSliceByRange(stdCode, period, stime, etime);
	}

	// 只有非基础周期的K线才需要缓存操作
	WTSSessionInfo* sInfo = get_session_info(stdCode, true);
	key = StrUtil::printf("%s-%u-%u", stdCode, period, times);
	BarCache& barCache = _bars_cache[key];
	barCache._period = period;
	barCache._times = times;
	if(barCache._bars == NULL)
	{
		// 第一次将全部数据缓存到内存中
		WTSKlineSlice* rawData = _reader->readKlineSliceByCount(stdCode, period, UINT_MAX, 0);
		if (rawData != NULL)
		{
			WTSKlineData* kData = g_dataFact.extractKlineData(rawData, period, times, sInfo, false);
			barCache._bars = kData;

			// 为了安全，先删除最后一根K线
			// 主要通过闭合标志判断，因为获取的基础周期可能还没有闭合
			if (barCache._bars->size() > 0)
			{
				auto& bars = barCache._bars->getDataRef();
				bars.erase(bars.begin() + bars.size() - 1, bars.end());
			}

			// 记录最后一根K线的时间
			if (period == KP_DAY)
				barCache._last_bartime = kData->date(-1);
			else
			{
				uint64_t lasttime = kData->time(-1);
				barCache._last_bartime = 199000000000 + lasttime;
			}

			rawData->release();
		}
		else
		{
			return NULL;
		}
	}
	else
	{
		// 增量更新缓存数据
		WTSKlineSlice* rawData = _reader->readKlineSliceByRange(stdCode, period, barCache._last_bartime, 0);
		if (rawData != NULL)
		{
			for(int32_t idx = 0; idx < rawData->size(); idx ++)
			{
				uint64_t barTime = 0;
				if (period == KP_DAY)
					barTime = rawData->at(0)->date;
				else
					barTime = 199000000000 + rawData->at(0)->time;
				
				// 只有时间大于上次记录的最后一个时间，才可能需要更新K线
				if(barTime <= barCache._last_bartime)
					continue;

				g_dataFact.updateKlineData(barCache._bars, rawData->at(idx), sInfo);
			}

			// 为了安全，先删除最后一根K线
			// 主要通过闭合标志判断，因为获取的基础周期可能还没有闭合
			if(barCache._bars->size() > 0)
			{
				auto& bars = barCache._bars->getDataRef();
				bars.erase(bars.begin() + bars.size() - 1, bars.end());
			}

			// 更新最后一根K线的时间
			if (period == KP_DAY)
				barCache._last_bartime = barCache._bars->date(-1);
			else
			{
				uint64_t lasttime = barCache._bars->time(-1);
				barCache._last_bartime = 199000000000 + lasttime;
			}
			

			rawData->release();
		}
	}

	// 最后到缓存中定位数据范围
	bool isDay = period == KP_DAY;
	uint32_t rDate, rTime, lDate, lTime;
	rDate = (uint32_t)(etime / 10000);
	rTime = (uint32_t)(etime % 10000);
	lDate = (uint32_t)(stime / 10000);
	lTime = (uint32_t)(stime % 10000);

	// 构造结束时间的K线结构体用于查找
	WTSBarStruct eBar;
	eBar.date = rDate;
	eBar.time = (rDate - 19900000) * 10000 + rTime;

	// 构造开始时间的K线结构体用于查找
	WTSBarStruct sBar;
	sBar.date = lDate;
	sBar.time = (lDate - 19900000) * 10000 + lTime;

	uint32_t eIdx, sIdx;
	auto& bars = barCache._bars->getDataRef();
	
	// 使用二分查找定位结束位置
	auto eit = std::lower_bound(bars.begin(), bars.end(), eBar, [isDay](const WTSBarStruct& a, const WTSBarStruct& b) {
		if (isDay)
			return a.date < b.date;
		else
			return a.time < b.time;
	});

	// 确定结束索引
	if (eit == bars.end())
		eIdx = bars.size() - 1;
	else
	{
		if ((isDay && eit->date > eBar.date) || (!isDay && eit->time > eBar.time))
		{
			eit--;
		}

		eIdx = eit - bars.begin();
	}

	// 使用二分查找定位开始位置
	auto sit = std::lower_bound(bars.begin(), eit, sBar, [isDay](const WTSBarStruct& a, const WTSBarStruct& b) {
		if (isDay)
			return a.date < b.date;
		else
			return a.time < b.time;
	});
	sIdx = sit - bars.begin();
	
	// 计算返回的数据条数和起始地址
	uint32_t rtCnt = eIdx - sIdx + 1;
	WTSBarStruct* rtHead = barCache._bars->at(sIdx);
	WTSKlineSlice* slice = WTSKlineSlice::create(stdCode, period, times, rtHead, rtCnt);
	return slice;
}

/**
 * @brief 按数量获取K线数据切片
 * @details 从指定结束时间向前获取指定数量的K线数据切片，支持K线缓存机制
 * @param stdCode 标准合约代码
 * @param period K线周期
 * @param times 周期倍数
 * @param count 数据数量
 * @param etime 结束时间（0表示最新）
 * @return WTSKlineSlice* K线数据切片
 */
WTSKlineSlice* WtDataManager::get_kline_slice_by_count(const char* stdCode, WTSKlinePeriod period, uint32_t times, uint32_t count, uint64_t etime /* = 0 */)
{
	std::string key = StrUtil::printf("%s-%u", stdCode, period);

	// 如果倍数为1，直接从数据读取器获取数据
	if (times == 1)
	{
		return _reader->readKlineSliceByCount(stdCode, period, count, etime);
	}

	// 只有非基础周期的K线才需要缓存操作
	WTSSessionInfo* sInfo = get_session_info(stdCode, true);
	key = StrUtil::printf("%s-%u-%u", stdCode, period, times);
	BarCache& barCache = _bars_cache[key];
	barCache._period = period;
	barCache._times = times;

	const char* tag = PERIOD_NAME[period-KP_Tick];

	if (barCache._bars == NULL)
	{
		// 第一次将全部数据缓存到内存中
		WTSLogger::info("Caching all %s bars of %s...", tag, stdCode);
		WTSKlineSlice* rawData = _reader->readKlineSliceByCount(stdCode, period, UINT_MAX, 0);
		if (rawData != NULL)
		{
			WTSLogger::info("Resampling %u %s bars by %u TO 1 of %s...", rawData->size(), tag, times, stdCode);
			WTSKlineData* kData = g_dataFact.extractKlineData(rawData, period, times, sInfo, true);
			barCache._bars = kData;

			// 对于非日线，要检查最后一根K线是否闭合的问题
			// 主要是按照返回的方法，如果当前时间小于最后一根K线的时间，则认为是还没闭合
			if (period != KP_DAY)
			{
				uint64_t last_bartime = 0;
				last_bartime = 199000000000 + kData->time(-1);

				uint64_t now = TimeUtils::getYYYYMMDDhhmmss() / 100;
				if (now <= last_bartime && barCache._bars->size() > 0)
				{
					auto& bars = barCache._bars->getDataRef();
					bars.erase(bars.begin() + bars.size() - 1, bars.end());
				}
			}

			// 记录最后一根K线的时间
			if (period == KP_DAY)
				barCache._last_bartime = kData->date(-1);
			else
			{
				uint64_t lasttime = kData->time(-1);
				barCache._last_bartime = 199000000000 + lasttime;
			}

			rawData->release();
		}
		else
		{
			return NULL;
		}
	}
	else
	{
		// 增量更新缓存数据
		WTSKlineSlice* rawData = _reader->readKlineSliceByRange(stdCode, period, barCache._last_bartime, 0);
		if (rawData != NULL)
		{
			WTSLogger::info("%u %s bars updated of %s, adding to cache...", rawData->size(), tag, stdCode);
			for (int32_t idx = 0; idx < rawData->size(); idx++)
			{
				uint64_t barTime = 0;
				if (period == KP_DAY)
					barTime = rawData->at(0)->date;
				else
					barTime = 199000000000 + rawData->at(0)->time;

				// 只有时间大于上次记录的最后一个时间，才可能需要更新K线
				if (barTime <= barCache._last_bartime)
					continue;

				g_dataFact.updateKlineData(barCache._bars, rawData->at(idx), sInfo);
			}

			// 对于非日线，要检查最后一根K线是否闭合的问题
			// 主要是按照返回的方法，如果当前时间小于最后一根K线的时间，则认为是还没闭合
			if (period != KP_DAY)
			{
				uint64_t last_bartime = 0;
				last_bartime = 199000000000 + barCache._bars->time(-1);

				uint64_t now = TimeUtils::getYYYYMMDDhhmmss() / 100;
				if (now <= last_bartime && barCache._bars->size() > 0)
				{
					auto& bars = barCache._bars->getDataRef();
					bars.erase(bars.begin() + bars.size() - 1, bars.end());
				}
			}

			// 更新最后一根K线的时间
			if (period == KP_DAY)
				barCache._last_bartime = barCache._bars->date(-1);
			else
			{
				uint64_t lasttime = barCache._bars->time(-1);
				barCache._last_bartime = 199000000000 + lasttime;
			}

			rawData->release();
		}
	}

	// 最后到缓存中定位数据范围
	bool isDay = period == KP_DAY;
	uint32_t rDate, rTime;
	rDate = (uint32_t)(etime / 10000);
	rTime = (uint32_t)(etime % 10000);

	// 构造结束时间的K线结构体用于查找
	WTSBarStruct eBar;
	eBar.date = rDate;
	eBar.time = (rDate - 19900000) * 10000 + rTime;

	uint32_t eIdx, sIdx;
	auto& bars = barCache._bars->getDataRef();
	
	// 使用二分查找定位结束位置
	auto eit = std::lower_bound(bars.begin(), bars.end(), eBar, [isDay](const WTSBarStruct& a, const WTSBarStruct& b) {
		if (isDay)
			return a.date < b.date;
		else
			return a.time < b.time;
	});

	// 确定结束索引
	if (eit == bars.end())
		eIdx = bars.size() - 1;
	else
	{
		if ((isDay && eit->date > eBar.date) || (!isDay && eit->time > eBar.time))
		{
			eit--;
		}

		eIdx = eit - bars.begin();
	}

	// 根据数量计算开始索引
	sIdx = (eIdx + 1 >= count) ? (eIdx + 1 - count) : 0;
	uint32_t rtCnt = eIdx - sIdx + 1;
	WTSBarStruct* rtHead = barCache._bars->at(sIdx);
	WTSKlineSlice* slice = WTSKlineSlice::create(stdCode, period, times, rtHead, rtCnt);
	return slice;
}

/**
 * @brief 按数量获取Tick数据切片
 * @details 从指定结束时间向前获取指定数量的Tick数据切片，时间戳转换为微秒精度
 * @param stdCode 标准合约代码
 * @param count 数据数量
 * @param etime 结束时间（0表示最新）
 * @return WTSTickSlice* Tick数据切片
 */
WTSTickSlice* WtDataManager::get_tick_slices_by_count(const char* stdCode, uint32_t count, uint64_t etime /* = 0 */)
{
	// 将时间戳转换为微秒精度
	etime = etime * 100000;
	return _reader->readTickSlicesByCount(stdCode, count, etime);
}